﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
//.NET socket libraries
using System.Net;
using System.Net.Sockets;
//for threading inclu threads safe method
using System.Threading;

using System.Diagnostics;

namespace EITSurferInfinityLib
{
    public class Ethernet
    {
        public IPAddress IPName {set;get;}
        public int Port { set; get; }

        public delegate void OnReceivedHandler(object sender, EventArgs e);
        public event OnReceivedHandler OnReceived;

        public delegate void OnConnectedHandler(object sender, EventArgs e);
        public event OnConnectedHandler OnConnected;

        public delegate void OnConnectionFailedHandler(object sender, EventArgs e);
        public event OnConnectionFailedHandler OnConnectionFailed;

        public delegate void OnMessageReceivedHandler(object sender, EventArgsMsg e);
        public event OnMessageReceivedHandler OnMessageReceived;

        public delegate void OnConnectionMessageReceivedHandler(object sender, EventArgsMsg e);
        public event OnConnectionMessageReceivedHandler OnConnectionMessageReceived;

        //Socket
        IAsyncResult m_asynResult;
        private AsyncCallback pfnCallBack;
        private Socket m_socClient;

        public string rx { set; get; }
        public int scanLength { set; get; }

        //buffers
        public List<byte> DataByte=new List<byte>();

        public Ethernet()
        {

        }
        public Ethernet(string myIPAddress, string myPort)
        {
            SetIPAddress(myIPAddress);
            int Port = System.Convert.ToInt16(myPort);
        }
        public Ethernet(string myIPAddress, int myPort)
        {
            SetIPAddress(myIPAddress);
            int Port = myPort;
        }

        public bool Send(string tx)
        {
            if (IsConnected())
            {
                tx = tx + "\n";
                try
                {
                    if (tx != null)
                    {
                        Object objData = tx;
                        byte[] byData = System.Text.Encoding.ASCII.GetBytes(objData.ToString());
                        m_socClient.Send(byData);
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                catch (SocketException se)
                {
                    SendConnectionMessage(se.Message);
                    Debug.WriteLine(se.Message);
                    return false;
                }
            }
            return false;
        }  

        public void ClearBuffer()
        {
            DataByte.Clear();
        }

        public void SetIPAddress(string myIPAddress)
        {
            IPName = CheckIP(myIPAddress);
        }

        private class CSocketPacket
        {
            public System.Net.Sockets.Socket thisSocket;
            public byte[] dataBuffer = new byte[8192];
        }

        public bool IsConnected()
        {
            if (m_socClient != null)
            {
                return m_socClient.Connected;
            }
            return false;
        }
        public void Connect()
        {
            try //command socket
            {
                Debug.WriteLine("connection infos: " + IPName.ToString()+", port: "+Port.ToString());
                m_socClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//create the socket instance
                m_socClient.Poll(10, SelectMode.SelectWrite);
                IPEndPoint ipEnd = new IPEndPoint(IPName, Port); //create the end point
                //m_socClient.Connect(ipEnd);                     //connect to the remote host...
                IAsyncResult result = m_socClient.BeginConnect(IPName, Port, null, null);
                //connect to the remote host...
                bool success = result.AsyncWaitHandle.WaitOne(1000, true);
                if (m_socClient.Connected)
                {
                    WaitForData();
                }
                else
                {
                    SendConnectionMessage("CONNECTION TIMEOUT");
                    Debug.WriteLine("CONNECTION TIMEOUT");
                }

                //WaitForData();                                 //watch for data ( asynchronously )...
            }
            catch (Exception se)
            {
                //Debug.WriteLine("connection failed: \n"+se.ToString());
                if (OnConnectionFailed != null)
                    OnConnectionFailed(null, null);
            }
        }

        public void Disconnect()
        {
            try
            {
                //if (m_socClient != null)
                //{
                    m_socClient.Close();
                    SendConnectionMessage("socket closed");
                    Debug.WriteLine("socket closed");
               // }
            }
            catch (Exception e)
            {
                SendConnectionMessage(e + "problem arises during socket deconnection");
                Debug.WriteLine(e + "problem arises during socket deconnection");
            }
        }
        private void WaitForData()
        {
            try
            {
                if (pfnCallBack == null)
                {
                    pfnCallBack = new AsyncCallback(OnDataReceived);
                }
                CSocketPacket theSocPkt = new CSocketPacket();
                theSocPkt.thisSocket = m_socClient;
                // now start to listen for any data...
                if (theSocPkt.thisSocket != null)
                {
                    m_asynResult = m_socClient.BeginReceive(theSocPkt.dataBuffer, 0, theSocPkt.dataBuffer.Length, SocketFlags.None, pfnCallBack, theSocPkt);
                }
            }
            catch (SocketException se)
            {
                SendConnectionMessage(se.Message);
                Debug.WriteLine(se.Message);
            }

        }

        private void OnDataReceived(IAsyncResult asyn)
        {
            try
            {
                CSocketPacket theSockId = (CSocketPacket)asyn.AsyncState;
                //end receive...
                int iRx = 0;
                iRx = theSockId.thisSocket.EndReceive(asyn);
                byte[] byteIn = new byte[iRx + 1];
                byteIn = theSockId.dataBuffer;
                if (byteIn != null && DataByte != null)
                {
                    for (int i = 0; i < iRx; i++)
                    {
                        DataByte.Add(byteIn[i]);
                    }
                    if (OnReceived != null)
                        OnReceived(null, null);
                }
                WaitForData();
            }
            catch (ObjectDisposedException)
            {
                System.Diagnostics.Debugger.Log(0, "1", "\nOnDataReceived: Socket has been closed\n");
            }
            catch (SocketException se)
            {
                SendConnectionMessage(se.Message);
                Debug.WriteLine(se.Message);
            }
        }

        private IPAddress CheckIP(string myIPAddress)
        {
            string input = myIPAddress;
            IPAddress address;

            if (IPAddress.TryParse(input, out address))
            {
                switch (address.AddressFamily)
                {
                    case System.Net.Sockets.AddressFamily.InterNetwork:
                        // we have IPv4
                        Debug.WriteLine("IPV4 address OK!");
                        SendConnectionMessage("IPV4 address OK!");
                        
                        break;
                    case System.Net.Sockets.AddressFamily.InterNetworkV6:
                        if (OnMessageReceived != null)
                            OnMessageReceived(null, new EventArgsMsg("IPCheck routine","IPV6 Address are not supported"));
                        break;
                    default:
                        // 
                        break;
                }
            }
            return address;
        }

        private void SendConnectionMessage(string s)
        {
            if (OnConnectionMessageReceived != null)
                OnConnectionMessageReceived(null, new EventArgsMsg("Connection Info",s));
        }
        
    }
}
